/*
 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *	  src/backend/utils/adt/inet_cidr_ntop.c
 */

#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $";
#endif

#include "postgres.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "utils/builtins.h"
#include "utils/inet.h"


#ifdef SPRINTF_CHAR
#define SPRINTF(x) strlen(sprintf/**/x)
#else
#define SPRINTF(x) ((size_t)sprintf x)
#endif

static char *fc_inet_cidr_ntop_ipv4(const u_char *fc_src, int fc_bits,
								 char *fc_dst, size_t fc_size);
static char *fc_inet_cidr_ntop_ipv6(const u_char *fc_src, int fc_bits,
								 char *fc_dst, size_t fc_size);

/*
 * char *
 * pg_inet_cidr_ntop(af, src, bits, dst, size)
 *	将网络号从网络格式转换为表示格式。
 *	始终生成 CIDR 风格的结果。
 * 返回：
 *	指向 dst 的指针，如果发生错误则为 NULL（检查 errno）。
 * 作者：
 *	Paul Vixie (ISC)，1996年7月
 */
char * pg_inet_cidr_ntop(int fc_af, const void *fc_src, int fc_bits, char *fc_dst, size_t fc_size)
{
	switch (fc_af)
	{
		case PGSQL_AF_INET:
			return fc_inet_cidr_ntop_ipv4(fc_src, fc_bits, fc_dst, fc_size);
		case PGSQL_AF_INET6:
			return fc_inet_cidr_ntop_ipv6(fc_src, fc_bits, fc_dst, fc_size);
		default:
			errno = EAFNOSUPPORT;
			return NULL;
	}
}


/*
 * static char *
 * inet_cidr_ntop_ipv4(src, bits, dst, size)
 *	将 IPv4 网络号从网络格式转换为表示格式。
 *	始终生成 CIDR 风格的结果。
 * 返回：
 *	指向 dst 的指针，如果发生错误则为 NULL（检查 errno）。
 * 注意：
 *	假定网络字节顺序。这意味着 192.5.5.240/28 在其第四个八位字节中有
 *	0b11110000。
 * 作者：
 *	Paul Vixie (ISC)，1996年7月
 */
static char * fc_inet_cidr_ntop_ipv4(const u_char *fc_src, int fc_bits, char *fc_dst, size_t fc_size)
{
	char	   *fc_odst = fc_dst;
	char	   *fc_t;
	u_int		fc_m;
	int			fc_b;

	if (fc_bits < 0 || fc_bits > 32)
	{
		errno = EINVAL;
		return NULL;
	}

	if (fc_bits == 0)
	{
		if (fc_size < sizeof "0")
			goto emsgsize;
		*fc_dst++ = '0';
		fc_size--;
		*fc_dst = '\0';
	}

	/* 格式化完整的八位字节。 */
	for (fc_b = fc_bits / 8; fc_b > 0; fc_b--)
	{
		if (fc_size <= sizeof "255.")
			goto emsgsize;
		fc_t = fc_dst;
		fc_dst += SPRINTF((fc_dst, "%u", *fc_src++));
		if (fc_b > 1)
		{
			*fc_dst++ = '.';
			*fc_dst = '\0';
		}
		fc_size -= (size_t) (fc_dst - fc_t);
	}

	/* 格式化部分八位字节。 */
	fc_b = fc_bits % 8;
	if (fc_b > 0)
	{
		if (fc_size <= sizeof ".255")
			goto emsgsize;
		fc_t = fc_dst;
		if (fc_dst != fc_odst)
			*fc_dst++ = '.';
		fc_m = ((1 << fc_b) - 1) << (8 - fc_b);
		fc_dst += SPRINTF((fc_dst, "%u", *fc_src & fc_m));
		fc_size -= (size_t) (fc_dst - fc_t);
	}

	/* 格式化 CIDR /宽度。 */
	if (fc_size <= sizeof "/32")
		goto emsgsize;
#ifdef FDD //cppcheck
	fc_dst += SPRINTF((fc_dst, "/%d", fc_bits));
#else
	fc_dst += SPRINTF((fc_dst, "/%u", fc_bits));
#endif
	return fc_odst;

emsgsize:
	errno = EMSGSIZE;
	return NULL;
}

/*
 * static char *
 * inet_cidr_ntop_ipv6(src, bits, dst, size)
 *	将 IPv6 网络号从网络格式转换为表示格式。
 *	始终生成 CIDR 风格的结果。除非 IP 真的是 IPv4，否则选择最短的表示。
 *	始终打印指定数量的位（bits）。
 * 返回：
 *	指向 dst 的指针，如果发生错误则为 NULL（检查 errno）。
 * 注意：
 *	假定网络字节顺序。这意味着 192.5.5.240/28 在其第四个八位字节中有
 *	0x11110000。
 * 作者：
 *	Vadim Kogan (UCB)，2001年6月
 *	原始版本（IPv4）由 Paul Vixie (ISC)，1996年7月
 */

static char * fc_inet_cidr_ntop_ipv6(const u_char *fc_src, int fc_bits, char *fc_dst, size_t fc_size)
{
	u_int		fc_m;
	int			fc_b;
	int			fc_p;
	int			fc_zero_s,
				fc_zero_l,
				fc_tmp_zero_s,
				fc_tmp_zero_l;
	int			fc_i;
	int			fc_is_ipv4 = 0;
	unsigned char fc_inbuf[16];
#ifdef FDD //cppcheck
	char		fc_outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")] = {'\0'};
#else
	char		fc_outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
#endif
	char	   *fc_cp;
	int			fc_words;
	u_char	   *fc_s;

	if (fc_bits < 0 || fc_bits > 128)
	{
		errno = EINVAL;
		return NULL;
	}

	fc_cp = fc_outbuf;

	if (fc_bits == 0)
	{
		*fc_cp++ = ':';
		*fc_cp++ = ':';
		*fc_cp = '\0';
	}
	else
	{
		/* 复制 src 到私有缓冲区。将主机部分归零。 */
		fc_p = (fc_bits + 7) / 8;
		memcpy(fc_inbuf, fc_src, fc_p);
		memset(fc_inbuf + fc_p, 0, 16 - fc_p);
		fc_b = fc_bits % 8;
		if (fc_b != 0)
		{
			fc_m = ((u_int) ~0) << (8 - fc_b);
			fc_inbuf[fc_p - 1] &= fc_m;
		}

		fc_s = fc_inbuf;

		/* 需要在输出中显示多少个单词 */
		fc_words = (fc_bits + 15) / 16;
		if (fc_words == 1)
			fc_words = 2;

		/* 找到零的最长子串 */
		fc_zero_s = fc_zero_l = fc_tmp_zero_s = fc_tmp_zero_l = 0;
		for (fc_i = 0; fc_i < (fc_words * 2); fc_i += 2)
		{
			if ((fc_s[fc_i] | fc_s[fc_i + 1]) == 0)
			{
				if (fc_tmp_zero_l == 0)
					fc_tmp_zero_s = fc_i / 2;
				fc_tmp_zero_l++;
			}
			else
			{
				if (fc_tmp_zero_l && fc_zero_l < fc_tmp_zero_l)
				{
					fc_zero_s = fc_tmp_zero_s;
					fc_zero_l = fc_tmp_zero_l;
					fc_tmp_zero_l = 0;
				}
			}
		}

		if (fc_tmp_zero_l && fc_zero_l < fc_tmp_zero_l)
		{
			fc_zero_s = fc_tmp_zero_s;
			fc_zero_l = fc_tmp_zero_l;
		}

		if (fc_zero_l != fc_words && fc_zero_s == 0 && ((fc_zero_l == 6) ||
											   ((fc_zero_l == 5 && fc_s[10] == 0xff && fc_s[11] == 0xff) ||
												((fc_zero_l == 7 && fc_s[14] != 0 && fc_s[15] != 1)))))
			fc_is_ipv4 = 1;

		/* 格式化完整的单词。 */
		for (fc_p = 0; fc_p < fc_words; fc_p++)
		{
			if (fc_zero_l != 0 && fc_p >= fc_zero_s && fc_p < fc_zero_s + fc_zero_l)
			{
				/* 该跳过一些零的时间 */
				if (fc_p == fc_zero_s)
					*fc_cp++ = ':';
				if (fc_p == fc_words - 1)
					*fc_cp++ = ':';
				fc_s++;
				fc_s++;
				continue;
			}

			if (fc_is_ipv4 && fc_p > 5)
			{
				*fc_cp++ = (fc_p == 6) ? ':' : '.';
				fc_cp += SPRINTF((fc_cp, "%u", *fc_s++));
				/* 我们可以潜在地去掉最后一个八位字节 */
				if (fc_p != 7 || fc_bits > 120)
				{
					*fc_cp++ = '.';
					fc_cp += SPRINTF((fc_cp, "%u", *fc_s++));
				}
			}
			else
			{
				if (fc_cp != fc_outbuf)
					*fc_cp++ = ':';
				fc_cp += SPRINTF((fc_cp, "%x", *fc_s * 256 + fc_s[1]));
				fc_s += 2;
			}
		}
	}
	/* 格式化 CIDR /宽度。 */
#ifdef FDD //cppcheck
	(void) SPRINTF((fc_cp, "/%d", fc_bits));
#else
	(void) SPRINTF((fc_cp, "/%u", fc_bits));
#endif
	if (strlen(fc_outbuf) + 1 > fc_size)
		goto emsgsize;
	strcpy(fc_dst, fc_outbuf);

	return fc_dst;

emsgsize:
	errno = EMSGSIZE;
	return NULL;
}
